/*
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.fieldscope.api;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * Builder objects use this to create unmodifiable maps. Builder objects
 * should use this internally and not expose it.
 * 
 * @author Travis Keep
 *
 * @param <K> The key type
 * @param <V> The value type
 */
class MapBuilder<K, V> {
  
  private Map<K, V> map;
  private boolean isReadOnly;
  
  /**
   * Create new object with empty underlying map.
   */
  public static <K, V> MapBuilder<K, V> newBuilder() {
    return new MapBuilder<K, V>();
  }
  
  /**
   * Clears underlying map.
   */
  public void clear() {
    setReadOnlyMap(Collections.<K, V>emptyMap());
  }
  
  /**
   * Adds key value pair to underlying map.
   */
  public void put(K key, V value) {
    makeMutable();
    map.put(key, value);
  }

  /**
   * Adds series of key value pairs to underlying map.
   */
  public void putAll(Map<? extends K,? extends V> map) {
    makeMutable();
    this.map.putAll(map);
  }

  /**
   * Build and return the unmodifiable map. If underlying map
   * is already unmodifiable then it is returned as is.
   */
  public Map<K, V> build() {
    makeReadOnly();
    return map;
  }
  
  /**
   * Sets the underlying map equaled to passed in map. Passed in map must
   * be unmodifiable. For performance reasons, this method only stores the
   * reference to passed in map. Only if a key value pair is added later will
   * this object make its own copy.
   * @param map an unmodifiable map.
   */
  public void setReadOnlyMap(Map<K, V> map) {
    this.map = map;
    isReadOnly = true;
  }
  
  private void setMutableMap(Map<K, V> map) {
    this.map = map;
    isReadOnly = false;
  }

  private void makeReadOnly() {
    if (!isReadOnly) {
      setReadOnlyMap(Collections.unmodifiableMap(map));
    }
  }
  
  private void makeMutable() {
    if (isReadOnly) {
      setMutableMap(new HashMap<K, V>(map));
    }
  }
  
  private MapBuilder() {
    clear();
  }
}
